/// <reference path = "./Math/math.ts"/>

namespace FIREFLYX {
    interface ITransformEventMap {
        "localPostion_set": Transform;
        "localRotation_set": Transform;
        "localScale_set": Transform;
        "postion_set": Transform;
        "rotation_set": Transform;
        "scale_set": Transform;
    }

    /**
     * 线性变换节点组件
     */
    export class Transform extends Component implements IEventListener {
        //private static field
        private static readonly helpV3Right = Vector3.Right();
        private static readonly helpV3UP = Vector3.Up();
        private static readonly helpV3Foward = Vector3.Forward();
        private static readonly helpMtx = new Matrix();
        private static readonly hlepQuat = new Quaternion();
        //public field

        //private field
        private _children: Transform[] = [];
        private _parent: Nullable<Transform>;
        private _localPosition: Vector3 = Vector3.Zero();
        private _localRotation: Quaternion = Quaternion.Identity();
        private _localScale: Vector3 = Vector3.One();
        private _localMatrix: Matrix = Matrix.Identity();
        private _localDirty: boolean = false;
        private _position: Vector3 = Vector3.Zero();
        private _rotation: Quaternion = Quaternion.Identity();
        private _scale: Vector3 = Vector3.One();
        private _matrix: Matrix = Matrix.Identity();
        private _dirty: boolean = false;
        private _ubo: Nullable<WebGLBuffer>;
        private _uboBlockSize: number = 4 * 16;
        private _uboDirty: boolean = false;
        private _eventDisp: Nullable<EventDispatcher>;

        /** 事件派发器 */
        private get eventDisp() {
            if (!this._eventDisp) {
                this._eventDisp = new EventDispatcher();
            }
            return this._eventDisp;
        }

        //get set
        /** 子节点数量 */
        public get childCount() { return this._children.length; }

        /** 父节点 */
        public get parent() { return this._parent; }
        public set parent(val) {
            if (this._parent == val) return;    //skip same val
            //双向绑定
            this._parent = val;
            if (!val) { //parent 是 null 等于在跟层级
                //为null 解绑
                if (this._parent) {
                    let i = this._parent._children.indexOf(this);
                    if (i != -1) {
                        this._parent._children.splice(i, 1);
                    }
                }
                //entity active 状态处理
                if (this.entity) {
                    this.entity.setActive(this.entity.activeSelf);
                }
            } else {
                val._children.push(this);

                //标记脏状态
                this._dirty = true;
                this.dirtyChildren();
                //entity active 状态处理
                if (this.entity && val.entity && val.entity.activeInHierarchy != this.entity.activeInHierarchy) {
                    this.entity.setActive(this.entity.activeSelf);
                }
            }
        }

        /** 节点名 */
        public get name() {
            if (!this.entity) return "";
            return this.entity.name;
        }
        public set name(val: string) {
            if (!this.entity) return;
            this.entity.name = val;
        }

        /** 本地坐标系中的坐标位置 */
        public get localPostion() { return this._localPosition; }
        public set localPostion(val) {
            if (!val) return;
            if (val != this._localPosition) {
                this._localPosition.copyFrom(val);
            }
            //dirty 处理
            this.dirty();

            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`localPostion_set`) > 0) {
                this.eventDisp.dispatch(`localPostion_set`, this);
            }
        }

        /** 本地坐标系中的旋转 */
        public get localRotation() { return this._localRotation; }
        public set localRotation(val) {
            if (!val) return;
            if (val != this._localRotation) {
                this._localRotation.copyFrom(val);
            }
            //dirty 处理
            this.dirty();

            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`localRotation_set`) > 0) {
                this.eventDisp.dispatch(`localRotation_set`, this);
            }
        }

        /** 本地坐标系中的缩放 */
        public get localScale() { return this._localScale; }
        public set localScale(val) {
            if (!val) return;
            if (val != this._localScale) {
                this._localScale.copyFrom(val);
            }
            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`localScale_set`) > 0) {
                this.eventDisp.dispatch(`localScale_set`, this);
            }
            //dirty 处理
            this.dirty();
        }

        /** 世界坐标系中的坐标位置 */
        public get position() { return this._position; }
        public set position(val) {
            if (!val) return;
            if (val != this._position) {
                this._position.copyFrom(val);
            }

            //调整 local
            if (this._parent) {
                //用父节点的逆矩阵算本地坐标系的位置
                let invMatrix = Transform.helpMtx;
                this._parent.getMatrix()
                    .invertToRef(invMatrix);
                Vector3.TransformCoordinatesToRef(val, invMatrix, this._localPosition);
            } else {
                this._localPosition.copyFrom(val);
            }

            //通过set触发local的修改
            this.localPostion = this._localPosition;

            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`postion_set`) > 0) {
                this.eventDisp.dispatch(`postion_set`, this);
            }
        }

        /** 世界坐标系中的旋转 */
        public get rotation() { return this._rotation; }
        public set rotation(val) {
            if (!val) return;
            if (val != this._rotation) {
                this._rotation.copyFrom(val);
            }

            //调整 local
            if (this._parent) {
                let invRotation = Transform.hlepQuat;
                invRotation.copyFrom(this._parent.rotation);
                Quaternion.InverseSelf(invRotation);
                invRotation.multiplyToRef(val, this._localRotation);
            } else {
                this._localRotation.copyFrom(val);
            }

            //通过set触发local的修改
            this.localRotation = this._localRotation;

            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`rotation_set`) > 0) {
                this.eventDisp.dispatch(`rotation_set`, this);
            }
        }

        /** 世界坐标系中的缩放 */
        public get scale() { return this._scale; }
        public set scale(val) {
            if (!val) return;
            if (val != this._scale) {
                this._scale.copyFrom(val);
            }

            //调整 local
            if (this._parent) {
                let pscale = this._parent.scale;
                this._localScale.set(val.x / pscale.x, val.y / pscale.y, val.z / pscale.z);
            } else {
                this._localScale.copyFrom(val);
            }

            //通过set触发local的修改
            this.localScale = this._localScale;

            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`scale_set`) > 0) {
                this.eventDisp.dispatch(`scale_set`, this);
            }
        }

        /** ubo块byte占用大小 */
        public get uboBlockSize() { return this._uboBlockSize; }

        /** model ubo */
        public get ubo() {
            let ubo = this._ubo;
            if (!ubo) {
                let gl = App.webgl2;
                ubo = this._ubo = gl.createBuffer();
                //bind start
                gl.bindBuffer(gl.UNIFORM_BUFFER, this._ubo);
                gl.bufferData(gl.UNIFORM_BUFFER, this._uboBlockSize, gl.DYNAMIC_DRAW);
                //bind end
                gl.bindBuffer(gl.UNIFORM_BUFFER, null);
                this._uboDirty = true;
            }

            return this._ubo;
        }
        //function

        public dispose() {
            super.dispose();

            //子节也做销毁处理
            for (let i = 0, len = this._children.length; i < len; i++) {
                let c = this._children[i];
                c.dispose();
            }

            //注销所有监听
            if (this._eventDisp) {
                this._eventDisp.offAll();
            }

            this._children.length = 0;
            //引用置空
            this._parent = null;
            (this._localPosition as any) = null;
            (this._localRotation as any) = null;
            (this._localScale as any) = null;
            (this._localMatrix as any) = null;
            (this._localDirty as any) = null;
            (this._position as any) = null;
            (this._rotation as any) = null;
            (this._scale as any) = null;
            (this._matrix as any) = null;
            (this._eventDisp as any) = null;
        }

        /** 本地坐标系中的变换矩阵 */
        public getLocalMatrix() {
            if (this._localDirty) {
                this.unDirtyLocal();
            }
            return this._localMatrix;
        }

        /** 世界坐标系中的变换矩阵 */
        public getMatrix() {
            if (this._dirty || this._localDirty) {
                this.unDirty();
            }
            return this._matrix;
        }

        /**
         * 当前节点在世界坐标系中的右方朝向
         * @param result 目标V3
         */
        public getRightToRef(result: Vector3) {
            if (!result) return;
            Vector3.TransformNormalToRef(Transform.helpV3Right, this.getMatrix(), result);
        }

        /**
         * 当前节点在世界坐标系中的上方朝向
         * @param result 目标V3
         */
        public getUpToRef(result: Vector3) {
            if (!result) return;
            Vector3.TransformNormalToRef(Transform.helpV3UP, this.getMatrix(), result);
        }

        /**
         * 当前节点在世界坐标系中的前方朝向
         * @param result 目标V3
         */
        public getForwardToRef(result: Vector3) {
            if (!result) return;
            Vector3.TransformNormalToRef(Transform.helpV3Foward, this.getMatrix(), result);
        }

        /**
         * 看向目标点
         * @param target 目标点
         */
        public lookatPoint(target: Vector3) {
            //看向目标
            let mtx = Transform.helpMtx;
            Matrix.LookAtLHToRef(this.position, target, Transform.helpV3UP, mtx);
            let quat = Transform.hlepQuat;
            quat.fromRotationMatrix(mtx);
            Quaternion.InverseSelf(quat);
            this.rotation.copyFrom(quat);
            this.rotation = this.rotation;
        }

        /**
         * 获取指定索引的子节点
         * @param index 子节点在队列中的索引
         * @returns 节点对象
         */
        public getChild(index: number): Nullable<Transform> {
            return this._children[index];
        }

        /** 卸下所有的子节点 */
        public detachChildren() {
            let cList = this._children;
            if (cList.length < 1) return;
            //子节点断开与自己的联系
            for (let i = 0, len = cList.length; i < len; i++) {
                let c = cList[i];
                c._parent = null;
            }
            //清空子节点容器
            cList.length = 0;
        }

        /**
         * 找指定名字的子节点
         * @param name 子节点名
         * @returns 节点对象
         */
        public findChild(name: string): Nullable<Transform> {
            if (name == this.name) return this;
            for (let i = 0, len = this._children.length; i < len; i++) {
                let node = this._children[i].findChild(name);
                if (node) return node;
            }
            return null;
        }

        /** 获取当前节点在父节点的孩子队列中的索引值 */
        public getSiblingIndex(): number {
            if (!this._parent) return -1;
            return this._parent._children.indexOf(this);
        }

        /**
         * 设置当前节点在父节点的孩子队列中位置
         * @param index 位置索引
         */
        public setSiblingIndex(index: number) {
            if (!this._parent || isNaN(index)) return;
            let cList = this._parent._children;
            let i = cList.indexOf(this);
            if (i == -1) return;
            index = Math.floor(index);
            if (index == i) return;                     //一样的位置
            let insetI = index < 0 ? 0 : index > cList.length - 1 ? cList.length - 1 : index;
            cList.splice(i);                            //从原来位置移除
            let offset = index < i ? 0 : -1;
            cList.splice(insetI + offset, 0, this);     //插入到新的位置
        }

        /**
         * 刷新 ubo 的数据
         * @returns
         */
        public refreshUboData() {
            if (!this._uboDirty) return;
            const ubo = this.ubo;
            if (!ubo) return;
            const gl = App.webgl2;
            //bind start
            gl.bindBuffer(gl.UNIFORM_BUFFER, ubo);
            //mMatrix
            gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.getMatrix().m);
            //bind end
            gl.bindBuffer(gl.UNIFORM_BUFFER, null);
            this._uboDirty = false;
        }

        public addListener<K extends keyof ITransformEventMap>(eventType: K, listener: (ev: ITransformEventMap[K]) => any, thisArg: any) {
            this.eventDisp.on(eventType, listener, thisArg);
        }

        public removeListener<K extends keyof ITransformEventMap>(eventType: K, listener: any, thisArg: any) {
            this.eventDisp.off(eventType, listener, thisArg);
        }

        /** 有变动脏化处理 */
        private dirty() {
            if (this._localDirty) return;   //已经标记过了.
            //自己标记脏状态
            this._uboDirty = this._localDirty = true;
            this.dirtyChildren();
        }

        /** 使子节点们变脏处理 */
        private dirtyChildren() {
            this._uboDirty = this._dirty = true;
            //传导到子节点
            for (let i = 0, len = this._children.length; i < len; i++) {
                let c = this._children[i];
                if (c._dirty) continue;     //该节点已经标记过了.
                c.dirtyChildren();
            }
        }

        /** 清理脏化状态 */
        private unDirty() {
            let localDirty = this._localDirty;
            this.unDirtyLocal();

            if (!localDirty && !this._dirty) return;
            //清理脏状态
            this._dirty = false;
            //使用更新计算世界空间矩阵.
            if (!this._parent) {
                this._matrix.copyFrom(this._localMatrix);
            } else {
                let pMatrix = this._parent.getMatrix();
                this._localMatrix.multiplyToRef(pMatrix, this._matrix);
            }

            //更新 世界坐标系的 位置、旋转、缩放
            this._matrix.decompose(this._scale, this._rotation, this._position);

            //派发事件
            if (this.eventDisp && this.eventDisp.listenerCount(`position_set`) > 0) {
                this.eventDisp.dispatch(`position_set`, this);
            }
            if (this.eventDisp && this.eventDisp.listenerCount(`rotation_set`) > 0) {
                this.eventDisp.dispatch(`rotation_set`, this);
            }
            if (this.eventDisp && this.eventDisp.listenerCount(`scale_set`) > 0) {
                this.eventDisp.dispatch(`scale_set`, this);
            }
        }

        /** 清理脏化本地坐标系状态,同步本地坐标系矩阵 */
        private unDirtyLocal() {
            if (!this._localDirty) return;
            //清理local 脏状态
            this._localDirty = false;
            //使用更新计算本地空间矩阵.
            Matrix.ComposeToRef(this._localScale, this._localRotation, this._localPosition, this._localMatrix);
        }
    }
}